package com.clear.honghefinance.biz.modules.system.member.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.alibaba.fastjson.JSON;
import com.clear.honghefinance.biz.constant.RedisKeyConstant;
import com.clear.honghefinance.biz.modules.system.member.converter.MemberConverter;
import com.clear.honghefinance.biz.modules.system.member.domain.entity.Member;
import com.clear.honghefinance.biz.modules.system.member.domain.vo.AdminDTO;
import com.clear.honghefinance.biz.modules.system.member.domain.vo.SmsCodeResult;
import com.clear.honghefinance.biz.modules.system.member.domain.dto.GetUserSmsCodeDTO;
import com.clear.honghefinance.biz.modules.system.member.domain.dto.UpdatePhoneDTO;
import com.clear.honghefinance.biz.modules.system.member.domain.vo.CurrentInfoVO;
import com.clear.honghefinance.biz.modules.system.member.service.IMemberService;
import com.clear.honghefinance.biz.modules.system.member.service.MemberBindPhoneService;
import com.clear.honghefinance.biz.modules.system.member.service.MemberComService;
import com.clear.honghefinance.common.exception.BizException;
import com.clear.honghefinance.common.exception.ParameterException;
import com.clear.honghefinance.common.service.TokenService;
import com.clear.honghefinance.common.util.DateUtil;
import com.clear.honghefinance.common.util.redis.RedisUtil;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import com.clear.honghefinance.biz.modules.system.member.enums.SmsCodeTypeEnum;
import com.clear.honghefinance.biz.modules.system.member.domain.entity.MemberBindPhone;

import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class MemberComServiceImpl implements MemberComService {

    private final IMemberService memberService;
    private final MemberBindPhoneService memberBindPhoneService;
    private final TokenService<AdminDTO> tokenService;
    private final RedisUtil redisUtil;
    private final RedissonClient redissonClient;

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public CurrentInfoVO getCurrentInfo() {
        Member member = memberService.get(tokenService.getThreadLocalUserId());
        if (Objects.isNull(member)) {
            throw new BizException("用户不存在");
        }
        if (member.getDisable()) {
            throw new BizException("用户被禁用");
        }
        return MemberConverter.INSTANCE.convertMemberEntityToCurrentInfoVO(member);
    }

    /**
     * 获取图形验证码（用于修改手机号）
     *
     * @return
     */
    @Override
    public String getBase64Code() {
        /*
         * width: 图片宽度
         * height: 图片高度
         * codeCount: 验证码个数
         * lineCount: 干扰线个数
         */
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(300, 192, 5, 1000);
        // 取出验证码
        String code = lineCaptcha.getCode();
        log.info("客户端member_id：{}，图形验证码：{}", tokenService.getThreadLocalUserId(), code);

        // 将验证码存入redis
        String key = redisUtil.buildKey(RedisKeyConstant.USER_GRAPHIC_VERIFICATION_CODE,
                tokenService.getThreadLocalUserId() + "");
        redisUtil.del(key);  // 清除旧的验证码
        redisUtil.setNx(key, code, 15L, TimeUnit.MINUTES);

        return lineCaptcha.getImageBase64();
    }

    /**
     * 发送短信验证码（用于修改手机号）
     *
     * @param dto
     */
    @Override
    public void sendSmsCode(GetUserSmsCodeDTO dto) {
        this.checkBase64Code(dto.getCode());

        String key = redisUtil.buildKey(RedisKeyConstant.SMS_CODE, dto.getSmsCodeType(), tokenService.getThreadLocalUserId() + "");
        // 获取缓存key，并使用gson反序列化为SmsCodeResult
        SmsCodeResult smsCodeResult = new Gson().fromJson(redisUtil.get(key), SmsCodeResult.class);
        if (Objects.nonNull(smsCodeResult)) {
            Duration duration = DateUtil.getDuration(smsCodeResult.getGetTime(), DateUtil.getSystemTime());
            if (duration.getSeconds() < 60) {
                throw new BizException("短信验证码已发送，请稍后再试");
            }
        }

        int smsCode = this.getRandom(6);
        smsCodeResult = new SmsCodeResult();
        smsCodeResult.setCode(String.valueOf(smsCode));
        smsCodeResult.setGetTime(DateUtil.getSystemTime());
        smsCodeResult.setPhone(dto.getPhone());

        redisUtil.setNx(key, new Gson().toJson(smsCodeResult), 15L, TimeUnit.MINUTES);

        log.info("用户id{},手机号：{},短信验证码：{}", tokenService.getThreadLocalUserId(), dto.getPhone(), smsCode);
        // 调用阿里云SMS发送验证码
        // todo 调用短信接口

    }


    /**
     * 修改手机号
     *
     * @param dto
     * @return
     */
    @Override
    public boolean updatePhone(UpdatePhoneDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("修改手机号中，入参：{}", JSON.toJSONString(dto));
        }
        RLock rLock = redissonClient.getLock(redisUtil.buildKey(RedisKeyConstant.PHONE_CHANGE, dto.getPhone()));
        try {
            rLock.lock();
            // 校验验证码
            checkSmsCode(dto.getPhone(), dto.getSmsCode(), SmsCodeTypeEnum.UPDATE_PHONE.getCode());
            // 查询手机号是否已存在
            MemberBindPhone memberBindPhone = memberBindPhoneService.getMemberByPhone(dto.getPhone());
            if (Objects.nonNull(memberBindPhone)) {
                throw new ParameterException("phone", "该手机号已注册！");
            }
            if (!memberBindPhoneService.updatePhone(dto)) {
                throw new BizException("修改手机号失败");
            }
        } catch (Exception e) {
            log.error("修改手机号失败{}", e.getMessage(), e);
            throw new RuntimeException("修改手机号失败", e);
        } finally {
            rLock.unlock();
        }
        return true;
    }

    private boolean checkSmsCode(String phone, String smsCode, String smsCodeType) {
        String key = redisUtil.buildKey(RedisKeyConstant.SMS_CODE, smsCodeType, phone);
        String cacheSmsCode = redisUtil.get(key);
        // 短信验证码只用一次：无论校验成功与否，都删除
        redisUtil.del(key);
        if (Objects.equals(cacheSmsCode, smsCode)) {
            throw new ParameterException("smsCode", "短信证码错误，请重新获取验证码！");
        }
        return true;
    }

    /**
     * 校验base64验证码
     *
     * @param code
     * @return
     */
    public boolean checkBase64Code(String code) {
        String key = redisUtil.buildKey(RedisKeyConstant.USER_GRAPHIC_VERIFICATION_CODE,
                tokenService.getThreadLocalUserId() + "");
        String cacheCode = redisUtil.get(key);
        // 图形验证码只用一次：无论校验成功与否，都删除
        redisUtil.del(key);
        if (!code.equalsIgnoreCase(cacheCode)) {
            throw new ParameterException("code", "图形验证码错误");
        }
        return true;
    }

    /**
     * 随机生成num位数字
     *
     * @param length 数字的长度
     * @return
     */
    private int getRandom(int length) {
        return (int) ((Math.random() * 9 + 1) * Math.pow(10, length - 1));
    }
}
